#include "Vec2.hpp"
#include <iostream>
#include <cmath>
#include <sstream>
#include <array>

using std::ostream;
using std::sqrt;
using std::ostringstream;
using std::array;

namespace gmath
{

  template<class T>
    Vec2<T>::Vec2()
    {
      data_.fill(0);
    }

  template<class T>
    Vec2<T>::Vec2(T value)
    {
      setData(value);
    }

  template<class T>
    Vec2<T>::Vec2(T x, T y)
    {
      setData(x, y);
    }

  template<class T>
    Vec2<T>::Vec2(T* ptr)
    {
      setData(ptr);
    }

  template<class T>
    const std::array<T, 2>& gmath::Vec2<T>::getData() const
    {
      return data_;
    }

  template<class T>
    void gmath::Vec2<T>::setData(const std::array<T, 2> data)
    {
      data_ = data;
    }

  template<class T>
    void gmath::Vec2<T>::setData(T value)
    {
      data_.fill(value);
    }

  template<class T>
    void gmath::Vec2<T>::setData(T x, T y)
    {
      data_[0] = x;
      data_[1] = y;
    }

  template<class T>
    void gmath::Vec2<T>::setData(T* ptr)
    {
      data_[0] = ptr[0];
      data_[1] = ptr[1];
    }

  template<class T>
    T Vec2<T>::length()
    {
      return sqrt(data_[0] * data_[0] + data_[1] * data_[1]);
    }

  template<class T>
    T Vec2<T>::dot(Vec2<T>& v2)
    {
      return data_[0] * v2.data_[0] + data_[1] * v2.data_[1];
    }

  template<class T>
    std::string Vec2<T>::toString()
    {
      ostringstream ss;
      ss << "{" << data_[0] << ", " << data_[1] << "}";
      return ss.str();
    }

  template<typename T>
    std::ostream& operator<<(std::ostream& out, Vec2<T> v)
    {
      out << v.toString();
      return out;
    }

  template<class T>
    Vec2<T> Vec2<T>::operator+(Vec2<T>& v2)
    {
      return Vec2<T>(data_[0] + v2.data_[0], data_[1] + v2.data_[1]);
    }

  template<class T>
    Vec2<T> Vec2<T>::operator-(Vec2<T>& v2)
    {
      return Vec2<T>(data_[0] - v2.data_[0], data_[1] - v2.data_[1]);
    }

  template<class T>
    Vec2<T> Vec2<T>::operator*(T a)
    {
      return Vec2<T>(data_[0] * a, data_[1] * a);
    }

  template<class T>
    Vec2<T> Vec2<T>::operator/(T a)
    {
      return Vec2<T>(data_[0] / a, data_[1] / a);
    }

  template<class T>
    Vec2<T> Vec2<T>::operator+=(Vec2<T>& v2)
    {
      data_[0] += v2.data_[0];
      data_[1] += v2.data_[1];
      return *this;
    }

  template<class T>
    Vec2<T> Vec2<T>::operator-=(Vec2<T>& v2)
    {
      data_[0] -= v2.data_[0];
      data_[1] -= v2.data_[1];
      return *this;
    }

  template<class T>
    Vec2<T> Vec2<T>::operator*=(T a)
    {
      data_[0] *= a;
      data_[1] *= a;
      return *this;
    }

  template<class T>
    Vec2<T> Vec2<T>::operator/=(T a)
    {
      data_[0] /= a;
      data_[1] /= a;
      return *this;
    }

  template ostream& operator<<(ostream& out, Vec2<float> v);
  template ostream& operator<<(ostream& out, Vec2<double> v);
  template ostream& operator<<(ostream& out, Vec2<int> v);
  template class Vec2<float> ;
  template class Vec2<double> ;
  template class Vec2<int> ;
}
